home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Alles Voor Internet / Tout Pour Internet
/
alles voor internet.iso
/
MacInternet™
/
Unix
/
maclayersunixend1.30.shar
/
1.30
/
layers.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-06-17
|
77KB
|
3,196 lines
/******** Layers.c
*********
********* Layers - MacLayers Multiwindow BSD Socket Driver
*********
********* David W. Trissel and Eric C. Rosen
*********
********* The sockets handling portion of this control module is based
********* upon 'screen' by Oliver Laumann whose copyright remains below.
********* The rest is
*
* Copyright (C) 1993 by Eric C. Rosen
* Copyright (C) 1989-1992 by David W. Trissel
*
* Not derived from licensed software.
*
* Permission is granted to freely use, copy, modify, and redistribute
* this software, provided that no attempt is made to gain profit from it,
* the authors are not construed to be liable for any results of using the
* software, alterations are clearly marked as such, and this notice is
* not modified.
*
*/
static char LayersVersion[] = "layers 1.30 17-May-1993";
/* Layers Changes:
Version 1.00 17-Mar-1990
First public release.
Version 1.01 10-May-1990
* Fixed bug 001 which caused failure of protocol mode to establish
if USRLIMIT check is enabled and USRLIMIT or more login shells
are already active. Could also keep new login layer windows from
responding until another layer is brought up front and then sent
back.
Version 1.01 27-Jun-1991
* Corrected bugs where utmp login record or initial tty settings
may not be restored if initial client connection fails
* Added return code support so shell scripts can determine what
happened:
0 - layers connected, ran and terminated sucessfully
1 - not running under MacLayers client terminal
2 - layers terminated due to some abnormal condition
3 - SIGQUIT or SIGTERM signal received
4 - SIGHUP signal received
* Rewrote protocl.c to avoid using scanf which was prohibiting 7-bit
communication lines from working under layers mode. (Scanf does
not work on 7-bit line in RAW mode since high bit invalidates
its character checking.)
* Removed internal TERMCAP entries for "ic" and "dc" (insert character
and delete character) which are not ANSI standard. The "dc" collided
with a real vt-100 capability that the author did not know about!
(Note of 6/11/91: perhaps "dc" and "ic" will be reinstated with
their true ANSI definitions. 6/11/91)
* If socket directory not 0700 then try to change it to 0700.
Version 1.10 26-Sep-1991
* Host/client version ID sharing at startup. This is transparent on
both sides with older versions.
1) If client identifies itself as at least 1.10 MacLayers
add vt102 TERMCAP entries for ic, dc, IC, DC with correct
capabilities. (V1.00 had incorrect ones-see above.)
Version 1.30 10-May-1993
* Added support for .layersrc file.
* Added support for A/UX, IRIX, AIX, ULTRIX, and NeXT systems;
should work on most System V platforms now with a few flags set.
* Cleaned up source (a little); removed some unused variables.
*/
/* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin.
* Not derived from licensed software.
*
* Permission is granted to freely use, copy, modify, and redistribute
* this software, provided that no attempt is made to gain profit from it,
* the author is not construed to be liable for any results of using the
* software, alterations are clearly marked as such, and this notice is
* not modified.
*
* Modified by Patrick Wolfe (pat@kai.com, kailand!pat)
* Do whatever you want with (my modifications of) this program, but
* don't claim you wrote them, don't try to make money from them, and
* don't remove this notice.
*/
/*
* Beginning of User Configuration Section
*
* You can set these variables from the command-line via the
* provided makefile. Type 'make' for details.
*/
/*
* GETTTYENT -- your system has the new format /etc/ttys (like 4.3 BSD)
* and the getttyent(3) library functions.
*
*/
/*
* These options are best set within the code. Don't change these unless
* you know what you are doing.
*/
/*
* LOGINDEFAULT -- if set to 1 (one), windows will login (add entries to
* /etc/utmp) by default. Set to 0 if you don't want this.
* (Also see USRLIMIT below). [NOTE: current code always
* logs layer #1 only unless -l option used on 'layers'
* command.]
*/
#define LOGINDEFAULT 0
/*
* USRLIMIT -- count all non-null entries in /etc/utmp before adding a
* new entry. Some machine manufacturers (incorrectly) count
* logins by counting non-null entries in /etc/utmp (instead
* of counting non-null entries with no hostname and not on
* a pseudo tty). Sequent does this, so you might reach your
* limited user license early.
*/
#define USRLIMIT 64
/*
* SOCKDIR -- If defined, this directory is where layers sockets will be
* placed, (actually in a subdirectory by the user's loginid).
* This is neccessary because NFS doesn't support socket
* operations, and many people's homes are on NFS mounted
* partitions. Layers will create this directory if it needs
* to.
*/
#define SOCKDIR "/tmp/layers" /* NFS doesn't support named sockets */
/*
* End of User Configuration Section
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Begin pre-defined environment definition section:
*/
/* Apple A/UX: */
#ifdef AUX
#define SYSVUTMP
#define USE_TERMIO
#undef USRLIMIT
#endif
/* Silicon Graphics IRIX: */
#ifdef IRIX
#define POSIXTTY
#define SYSVUTMP
#define IGNORE_SIGHUP
#undef GETTTYENT
#undef USRLIMIT
#endif
/* NeXT: */
#ifdef NeXT
#define GETTTYENT
#endif
/* IBM AIX: */
#ifdef AIX
#define USE_TERMIO
#define IGNORE_SIGHUP
#endif
/* DEC ULTRIX: */
#ifdef ULTRIX
#define GETTTYENT
#endif
/* Data General Aviion: */
#ifdef DG_AVIION
#define _BSD_WAIT_FLAVOR 1 /* to avoid wait3 WIFSTOPPED macro error */
#define USE_TERMIO
#define IGNORE_SIGHUP
#endif
/* System V machines use sigset instead of signal to auto-reactivate handlers */
#ifdef DG_AVIION
#define SIGNAL(a,b) sigset(a,b)
#endif
#ifdef SVR4
#define POSIXTTY
#undef USRLIMIT
#define index strchr
#endif
#ifndef SIGNAL
#define SIGNAL(a,b) signal(a,b)
#endif
/*
* End of predefined envirnment configuration section.
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Begin include-files:
*/
#if defined(IRIX) || defined(SVR4)
#include <unistd.h>
#endif
#include <stdio.h>
#ifdef NeXT
#include <libc.h>
#endif
#include <signal.h>
#ifdef __STRICT_BSD__
#define SIGTYPE int
#else
#define SIGTYPE void
#endif
/*
* Some systems do not have <string.h>; others don't have <strings.h>:
*/
#if !defined(SEQUENT)
#include <string.h>
#endif
#if !defined(SVR4)
#include <strings.h>
#endif
#include <errno.h>
#include <ctype.h>
#include <utmp.h>
#include <pwd.h>
#include <nlist.h>
#include <fcntl.h>
#include <sys/types.h>
#ifndef FD_SETSIZE
typedef int fd_set;
#endif
#include <sys/time.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/dir.h>
#ifndef POSIXTTY
#include <sys/ioctl.h>
#endif
#ifdef SVR4
#include <utmpx.h>
#include <sys/stropts.h>
#endif
#include "layers.h"
#ifdef GETTTYENT
#include <ttyent.h>
#else
static struct ttyent
{ char *ty_name;
} *getttyent();
static char *tt, *ttnext;
static char ttys[] = "/etc/ttys";
#endif
#ifndef FSCALE
#define FSCALE 1000.0 /* Sequent doesn't define FSCALE...grrrr */
#endif
#ifdef USRLIMIT
struct utmp utmpbuf;
int UserCount;
#endif
#define Ctrl(c) ((c)&037)
/* C library items */
extern char **environ;
extern errno;
extern sys_nerr;
extern char *sys_errlist[];
#ifndef NeXT
extern char *malloc(), *getenv();
extern char *getlogin(), *ttyname();
#endif
/* Local items */
static SIGTYPE FAbort(), SigHup(), SigChld();
static void AddCap(), FinitTerm();
static char *MakeTermcap(), *Filename(), **SaveArgs(), *GetTtyName(); /* [SaveArgs UNUSED] */
static void InitWorld(), ClearShape(), BuildTitle(), KillPG();
static void SetWindowSize(), RemoveTtyFromUtmp(), RestoreRealUtmp(), InitUtmp();
static int SetUtmp();
static int ProcessGeo();
static int loginflag = -1;
static char PtyName[32], TtyName[32];
static char *ShellProg;
static char *ShellArgs[2];
static inlen; /* [UNUSED] */
static ESCseen; /* [UNUSED] */
static GotSignal;
static char DefaultShell[] = "/bin/sh";
static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";
static char PtyProto[] = "/dev/ptyXY";
static char TtyProto[] = "/dev/ttyXY";
static int TtyMode = 0622;
static struct stat RealTtyStat; /* Real tty stat */
static char RealTtyName[32] = ""; /* Real tty name */
static int RealTtyMode = 0; /* Real tty mode */
static int Oumask; /* Original user's umask */
static char SockPath[512];
#ifdef SOCKDIR
static char SockDir[] = SOCKDIR;
#else
static char SockDir[] = ".layers";
#endif
static char *SockNamePtr, *SockName;
static ServerSocket;
static char *NewEnv[MAXARGS];
static char Esc = Ctrl('a'); /* [UNUSED] */
static char MetaEsc = 'a'; /* [UNUSED] */
static char *home;
static Abortonmsg = 0;
static Resetenvonmsg = 0;
static utmp, utmpf;
static char *LoginName;
static mflag, nflag, fflag;
static rflag; /* [UNUSED] */
static char HostName[MAXSTR];
static char *myname;
static DevTty;
#ifdef POSIXTTY
#include <termios.h>
static struct termios OldMode, NewMode;
#ifdef IRIX
#define SetTTY(fd, mode) ioctl(fd, TCSETA, mode)
#define GetTTY(fd, mode) ioctl(fd, TCGETA, mode)
#else
#define SetTTY(fd, mode) ioctl(fd, TCSETS, mode)
#define GetTTY(fd, mode) ioctl(fd, TCGETS, mode)
#endif /* IRIX */
#else
#ifdef USE_TERMIO
#include <termio.h>
struct termio OldMode, NewMode;
#else
#include <sgtty.h>
static struct mode {
struct sgttyb m_ttyb;
struct tchars m_tchars;
struct ltchars m_ltchars;
int m_ldisc;
int m_lmode;
} OldMode, NewMode;
#endif /* AIX */
#endif /* POSIXTTY */
#define MSG_CREATE 0
#define MSG_ERROR 1
struct msg
{
int type;
union
{ struct
{ int lflag; /* login flag */
struct Shape shape; /* window shape */
int nargs;
char line[MAXLINE];
char dir[1024];
} create;
char message[MAXLINE];
} m;
};
/* dynamic keyboard input buffer definition */
struct Kbuff
{ struct Kbuff * next; /* next buffer in chain (or NULL) */
int size; /* size of data in this buffer */
int offset; /* start of first data in buffer */
unsigned char text[IOSIZE]; /* text buffer itself */
};
/* World layer definition */
struct Layer {
int chan; /* channel represented by this layer */
int allocated; /* layer allocated */
int ptyfd; /* psuedo tty */
int ptymask; /* mask for pty descriptor */
int lpid; /* layer head process ID */
int slot; /* utmp slot number */
struct Kbuff *kbuff; /* keyboard input buffers */
struct Shape shape; /* Shape structure to/from MacLayers */
char cmd[MAXSTR]; /* command to execute */
char tty[MAXSTR]; /* psuedo tty ID */
};
static struct Layer World[MAXPCHAN]; /* all layer structures */
static int rows = 24; /* default window height in lines */
static int cols = 80; /* default window width in chars */
static char Termcap[1024];
static char Term[MAXSTR] = "TERM="; /* window's terminal type */
static char *UserTerm; /* terminal ID we are mimmicing */
static int flowctl;
static tcLineLen = 100;
#define EXITNORMAL 0 /* normal exit - connection succeeded */
#define EXITNOMACLAYERS 1 /* not running to MacLayers terminal */
#define EXITABNORMAL 2 /* something wrong generic error */
#define EXITSIGQUIT 3 /* TERM or QUIT signal got us */
#define EXITSIGHUP 4 /* HUP signal got us */
static exitcode = EXITABNORMAL; /* default to abnormal exit */
/* co#80 and li$24 added dynamically for proper window size */
static char TermcapConst1[] = "TERMCAP=SC|";
#ifdef IRIX
static char TermcapConst3[] = "|MacLayers virtual terminal|\\\n\
:cr=^M:do=^J:nl=^J:bl=^G:cl=\\E[;H\\E[2J:\\\n\
:le=^H:bs:am:cm=\\E[%i%d;%dH:nd=\\E[C:up=\\E[A:\\\n\
:ce=\\E[K:cd=\\E[J:so=\\E[7m:se=\\E[m:us=\\E[4m:ue=\\E[m:\\\n\
:md=\\E[1m:mr=\\E[7m:mb=\\E[5m:me=\\E[m:is=\\E[1;24r\\E[24;1H:\\\n\
:rf=/usr/lib/tabset/vt100:\\\n\
:rs=\\E>\\E[?3l\\E[?4l\\E[?5l\\E[?7h\\E[?8h:ks=\\E[?1h\\E=:ke=\\E>:\\\n\
:ku=\\EOA:kd=\\EOB:kr=\\EOC:kl=\\EOD:kb=^H:\\\n\
:ho=\\E[H:k1=\\EOP:k2=\\EOQ:k3=\\EOR:k4=\\EOS:ta=^I:pt:sr=\\EM:vt#3:xn:\\\n\
:sc=\\E7:rc=\\E8:cs=\\E[%i%d;%dr:\\\n\
:";
#else
static char TermcapConst3[] = "|MacLayers virtual terminal|\\\n\
:cr=^M:do=^J:nl=^J:bl=^G:cl=\\E[;H\\E[2J:\\\n\
:le=^H:bs:am:cm=\\E[%i%d;%dH:nd=\\E[C:up=\\E[A:\\\n\
:ce=\\E[K:cd=\\E[J:so=\\E[7m:se=\\E[m:us=\\E[4m:ue=\\E[m:\\\n\
:md=\\E[1m:mr=\\E[7m:mb=\\E[5m:me=\\E[m:is=\\E[1;24r\\E[24;1H:\\\n\
:rf=/usr/lib/tabset/vt100:\\\n\
:rs=\\E>\\E[?3l\\E[?4l\\E[?5l\\E[?7h\\E[?8h:ks=\\E[?1h\\E=:ke=\\E[?1l\\E>:\\\n\
:ku=\\EOA:kd=\\EOB:kr=\\EOC:kl=\\EOD:kb=^H:\\\n\
:ho=\\E[H:k1=\\EOP:k2=\\EOQ:k3=\\EOR:k4=\\EOS:ta=^I:pt:sr=\\EM:vt#3:xn:\\\n\
:sc=\\E7:rc=\\E8:cs=\\E[%i%d;%dr:\\\n\
:";
#endif
/* The following supported by MacLayers versions 1.1 and later */
/* ANSI insert characters and delete characters */
static char TermcapConst101[] = "ic=\\E[@:dc=\\E[P:IC=\\E[%d@:DC=\\E[%dP:";
int Dflag; /* debug dump flag */
int Clientlevel = 1; /* client protocol (Default to V1.00) */
int Protocollevel = 1; /* effective protocol (default 1) */
#ifdef IRIX
static void ignore_alarm()
{
signal(SIGALRM, ignore_alarm);
}
#endif
#ifdef SVR4
#include <sys/resource.h>
static int getdtablesize()
{
struct rlimit rl;
(void) getrlimit(RLIMIT_NOFILE, &rl);
return rl.rlim_cur;
}
#endif
/* main() */
int
main(ac, av)
int ac;
char **av;
{
extern int Initlink();
void Msg(), SigHandler();
static void SendCreateMsg(), MakeNewEnv();
static SetMode();
#ifndef POSIXTTY
static void GetTTY(), SetTTY();
#endif
static int GetSockName(), MakeClientSocket(), MakeServerSocket(),
MakeWindow(), ReadRc();
static void ReceiveMsg();
register n;
register len;
register struct Layer *layer;
char *ap;
char *clienttitle = NULL;
struct passwd *ppp;
int s;
int r; /* read fd test bits */
int w; /* write fd test bits */
int stall; /* stall flag for priority channel */
int fderr; /* error fd test bits */
struct timeval tv;
struct Shape shape; /* window shape */
char buf[IOSIZE];
char rc[256];
struct stat st;
struct Kbuff *kbptr; /* input keyboard buffer pointer */
time_t now; /* [UNUSED] */
Abortonmsg = 1; /* quit if message generated */
ClearShape(&shape); /* initialize shape structure */
myname = (ac == 0) ? "layers" : av[0];
InitWorld(); /* clear World array structures */
while (ac > 0)
{ ap = *++av;
if (--ac > 0 && *ap == '-')
{ switch (ap[1])
{ case 'l': /* login this command line */
loginflag = 1;
break;
case 'd': /* dump debugging flag */
Dflag = 1;
(void) freopen("layers.dump", "a", stderr); /* append mode */
#ifdef SEQUENT
setlinebuf(stderr);
#else
setvbuf(stderr, NULL, _IOLBF, 0);
#endif
break;
case 'u': /* do not login this command line */
loginflag = 0;
break;
case 'm': /* force this to be master and not a client */
mflag = 1;
break;
case 'n': /* no flow control */
nflag = 1;
break;
case 'f': /* flow control on */
fflag = 1;
break;
case 'v': /* do nothing but issue layers version */
printf("%s\n", LayersVersion);
exit(EXITNORMAL);
/*
case 't':
if (ac > 1) {
clienttitle = *(++av);
ac--;
}
break;
*/
case 'g':
if (ac > 1) {
if (ProcessGeo(&shape, *(++av))) {
printf("%s: invalid geometry argument \"%s\"\n", myname, *av);
exit(EXITABNORMAL);
}
ac--;
}
break;
default:
help:
Msg (0,"Use: %s [-f] [-g geometry-str] [-l | -u] [-m] [-n] [cmd args]\n", myname);
} /* end switch on '-' option */
} /* end if '-' */
else
break;
} /* end while parameters */
if (nflag && fflag)
Msg (0, "-f and -n are conflicting options.");
if ((ShellProg = getenv ("SHELL")) == 0)
ShellProg = DefaultShell;
DO DEBUG("ShellProg %s\n", ShellProg);
/* we mimmic the user's $TERM ID */
if ((UserTerm = getenv("TERM")) == 0)
UserTerm = "layers"; /* use "layers" if none */
(void) strcat(Term, UserTerm);
DO DEBUG("%s\n", Term);
ShellArgs[0] = ShellProg;
if (ac == 0)
{ ac = 1;
av = ShellArgs;
shape.wattr |= Wa_shell; /* indicate a shell window */
}
if ((home = getenv ("HOME")) == 0)
Msg (0, "$HOME is undefined.");
DO DEBUG("home %s\n", home);
if ((LoginName = getlogin ()) == NULL || LoginName[0] == '\0')
{
if ((ppp = getpwuid(getuid())) == 0) {
Msg(0, "Can't get login-name from passwd file.");
exit(EXITABNORMAL);
}
LoginName = ppp->pw_name;
}
DO DEBUG("LoginName %s\n", LoginName);
if ((Oumask=umask(0)) == -1)
Msg (errno, "Cannot change umask to zero");
DO DEBUG("Original umask o%o\n", Oumask);
#ifdef SOCKDIR
if (stat (SOCKDIR, &st) == -1)
{ if (errno == ENOENT)
{ if (mkdir (SOCKDIR, 0777) == -1)
Msg (errno, "Cannot make directory %s", SOCKDIR);
(void) chown (SOCKDIR, 0, 0);
}
else
Msg (errno, "Cannot get status of %s", SOCKDIR);
}
else
{ if ((st.st_mode & S_IFMT) != S_IFDIR)
Msg (0, "%s is not a directory.", SOCKDIR);
if ((st.st_mode & 0777) != 0777)
Msg (0, "Directory %s must have mode 777.", SOCKDIR);
}
sprintf (SockPath, "%s/%s", SockDir, LoginName);
#else
sprintf (SockPath, "%s/%s", home, SockDir);
#endif
DO DEBUG("SockPath %s\n", SockPath);
if (stat (SockPath, &st) == -1)
{ if (errno == ENOENT)
{ if (mkdir (SockPath, 0700) == -1)
Msg (errno, "Cannot make directory %s", SockPath);
(void) chown (SockPath, getuid (), getgid ());
DO DEBUG("SockPath directory made\n");
}
else
Msg (errno, "Cannot get status of %s", SockPath);
}
else
{ if ((st.st_mode & S_IFMT) != S_IFDIR)
Msg (0, "%s is not a directory.", SockPath);
if ((st.st_mode & 0777) != 0700)
{ if (chmod(SockPath, 0700) == -1)
Msg(0, "Cannot change directory %s to mode 700.", SockPath);
}
if (st.st_uid != getuid ())
Msg (0, "You are not the owner of %s.", SockPath);
}
(void) strcpy(RealTtyName, GetTtyName()); /* real tty name */
if (stat(RealTtyName, &RealTtyStat) == -1) /* get current mode */
Msg(errno, "Cannot get status of %s", RealTtyName);
DO DEBUG("Mode of %s is %#o\n", RealTtyName, RealTtyStat.st_mode);
RealTtyMode = RealTtyStat.st_mode; /* save mode for later restore */
(void) gethostname (HostName, MAXSTR);
HostName[MAXSTR-1] = '\0';
DO DEBUG("HostName %s\n", HostName);
if (ap = index (HostName, '.'))
*ap = '\0';
if (ap)
DO DEBUG("*ap %s\n", *ap);
strcat (SockPath, "/");
SockNamePtr = SockPath + strlen (SockPath);
/* if we are a client send create message to startup window and exit */
if (GetSockName ())
{ DO DEBUG("GetSockName says that we are client\n");
DO DEBUG("SockName '%s'\n", SockName);
s = MakeClientSocket (1);
DO DEBUG("Client socket is %d\n", s);
DO DEBUG("SendCreateMsg()\n");
if (clienttitle) {
av[1] = clienttitle; /* won't work; tries to run it (of course) */
SendCreateMsg(s, 2, av, loginflag, &shape);
} else
SendCreateMsg (s, ac, av, loginflag, &shape);
close (s);
DO DEBUG("after SendCreateMsg(), now exit(EXITNORMAL)\n");
exit (EXITNORMAL);
}
/* we are the server */
DO DEBUG("SockName '%s'\n", SockName);
DO DEBUG("We are server\n");
#ifndef SVR4
if ((DevTty = open ("/dev/tty", O_RDWR|O_NDELAY)) == -1)
Msg (errno, "/dev/tty");
DO DEBUG("opened /dev/tty fd %d\n", DevTty);
#endif
ServerSocket = MakeServerSocket ();
DO DEBUG("ServerSocket %d\n", ServerSocket);
s = ServerSocket;
if (fflag)
flowctl = 1;
else
if (nflag)
flowctl = 0;
if (loginflag == -1)
loginflag = LOGINDEFAULT;
MakeNewEnv ();
Resetenvonmsg = 1; /* cleanup Utmp and tty if abort */
GetTTY (0, &OldMode);
SetMode (&OldMode, &NewMode);
SetTTY (0, &NewMode);
InitUtmp ();
/*
* On some systems, have noticed SIGHUP sent to server whenever a client
* terminates, thus terminating the *server* prematurely.
*
* Fix this by ignoring SIGHUP on such systems ... (may no longer be needed?)
*/
#ifdef IGNORE_SIGHUP
signal (SIGHUP, SIG_IGN); /* ignore SIGUP */
#else
signal (SIGHUP, SigHup);
#endif
signal (SIGINT, SIG_IGN); /* we should never see this */
signal (SIGQUIT, FAbort); /* quit layers on these 2 signals */
signal (SIGTERM, FAbort);
signal (SIGTTIN, SIG_IGN);
signal (SIGTTOU, SIG_IGN);
#ifdef IRIX
signal (SIGALRM, ignore_alarm); /* ignore alarm under IRIX */
#else
signal (SIGALRM, SIG_IGN); /* alarm clock used by protocol.c */
#endif
if (Initlink() == 0) {
exitcode = EXITNOMACLAYERS; /* return special status code */
Msg (0, "\n\n You are not running under MacLayers.");
DO DEBUG("Initlink() unsuccesful.");
} else
DO DEBUG("Initlink() successful.");
/*
* startup at least one default shell window:
*/
n = MakeWindow (0, *av, av, (char *)0, loginflag, &shape);
if (n < 0) {
SetTTY(0, &OldMode);
FQuit(EXITABNORMAL);
/* NOT REACHED */
}
DO DEBUG("MakeWindow() == %d", n);
(void) chmod(RealTtyName, 0600); /* lock out broadcasts */
Abortonmsg = 0; /* don't abort on msg from now on */
Resetenvonmsg = 0; /* no cleanup - messages normal */
#ifdef SVR4
{
struct sigaction sa;
sa.sa_handler = SigChld;
(void) sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NOCLDWAIT | SA_SIGINFO;
(void) sigaction(SIGCHLD, &sa, NULL);
}
#else
#ifdef SYSV
signal(SIGCLD, SigChld);
#else
signal (SIGCHLD, SigChld);
#endif
#endif
tv.tv_usec = 0;
tv.tv_sec = 0; /* default no timer wanted */
/*
* now, check for existence of .layersrc file;
* if it exists, use it:
*/
sprintf (rc, "%.*s/.layersrc", sizeof(rc)-12, home);
n = 1 + ReadRc(rc);
/* client/Maclayers processing loop */
/* poll .20 secs for new startups */
#define WUSSTARTUP 200000
/* stall nonpriority windows .5 seconds when top window I/O active */
#define WUSSTALL 500000
stall = 0; /* startout no stalled channels */
fderr = 0; /* startout no error fd's */
while (1)
{ int priochan; /* the top window channel */
priochan = TopChannel(); /* find highest priority channel */
/* check for I/O on all available I/O descriptors */
r = 1<<0; /* always read MacLayers stream */
r |= 1<<s; /* always read server socket */
w = 0; /* initalize to no write tests */
tv.tv_usec = 0; /* default no startup poll */
/* for all active layers set read and write bits as appropriate */
if (stall)
stall = 1; /* start counting output layers */
for (n=0; n<MAXPCHAN; n++)
if ((layer = &World[n])->allocated) /* if layer exists ... */
{ /* if not yet started or has just terminated ... */
if (layer->ptymask & fderr)
tv.tv_usec = WUSSTARTUP; /* don't spinloop but wait-a-bit */
else
{ if (layer->kbuff && layer->kbuff->size)
/* keyboard input for layer */
w |= layer->ptymask; /* try write to it */
/* read layer output unless we're being nice to top window */
if (!stall)
r |= layer->ptymask; /* read output from layer */
else
if (layer->chan == priochan)
r |= layer->ptymask; /* read top priority output */
else
stall++; /* indicate something to stall */
}
}
if (stall > 1)
if (!tv.tv_usec)
tv.tv_usec = WUSSTALL; /* set stall timout */
/* process signals before trying select */
if (GotSignal)
{ SigHandler ();
continue;
}
DO DEBUG("Select(r %x, w %x, fderr %x, stall %d, prio %d, us %d)\n",
r, w, fderr, stall, priochan, tv.tv_usec);
switch ( select(32, (fd_set *) &r, (fd_set *) &w, (fd_set *) NULL,
tv.tv_usec ? &tv : (struct timeval *) NULL) )
{ case -1:
/* errno has report */
if (errno == EINTR) /* signal delivered or timout */
{ errno = 0;
tv.tv_usec = 0; /* clear timer wait value */
fderr = 0; /* turn off error stall */
stall = 0; /* turn off output priority stall */
DO DEBUG("select errno EINTR\n");
continue; /* re-loop */
}
Abortonmsg = 1;
Resetenvonmsg = 1; /* clean up tty settings */
DO DEBUG("select errno %d\n", errno);
Msg (errno, "select");
/*NOTREACHED*/
case 0:
/* timeout reached */
tv.tv_usec = 0; /* clear timer wait value */
stall = 0; /* turn off stall */
fderr = 0; /* turn off error stall */
continue; /* re-loop */
default:
/* a channel has read/write status pending */
break;
}
DO DEBUG("after select r %x w %x\n", r, w);
/* handle any signal arriving up during select wait */
if (GotSignal)
{ SigHandler ();
continue;
}
/* if server socket has command process that now */
if (r & 1 << s)
{ ReceiveMsg(s); /* process client control packet */
continue; /* status may have changed */
}
/* next process input stream from MacLayers */
if (r & 1 << 0)
{ ProcessStreamin(); /* key input and control packets */
continue; /* status may have changed */
}
/* process keyboard input first so output doesn't hold up
** keyboard echo and break/interrupt processing
*/
priochan = TopChannel(); /* find top priority channel */
stall = 0; /* assume no stall needed */
for (n=0; n<MAXPCHAN; n++)
if ((layer = &World[n])->allocated)
if (w & layer->ptymask)
while ((kbptr=layer->kbuff)->size)
{ /* pass as much keyboard as possible */
if (layer->chan == priochan)
stall = 1; /* stall lower priority channels */
len = write(layer->ptyfd, &kbptr->text[kbptr->offset],
kbptr->size);
DO DEBUG("keyin len %d to chan %d\n", len, layer->chan);
if (len <= 0) /* if no data accepted ... */
{ if (errno == EIO) /* if I/O error indicated ... */
fderr |= layer->ptymask; /* wait-a-bit on this */
errno = 0; /* clear errno */
break; /* try again later */
}
/* some of buffer accepted */
kbptr->size -= len; /* length processed */
kbptr->offset += len; /* bump up offset */
if (kbptr->size > 0) /* not all buffer accepted ... */
break; /* try feed again later */
/* see if another buffer chained */
if (kbptr->next)
{ /* yes, free up current buffer and queue next */
layer->kbuff = kbptr->next; /* to next buffer */
free(kbptr); /* free this buffer up */
}
else
{ /* otherwise leave this for next input */
kbptr->size = 0; /* empty buffer */
kbptr->offset = 0; /* refill from the start */
}
}
/* first process the highest priority channel (top window) */
if (priochan > 0 && priochan <= MAXPCHAN) /* if valid ... */
if ((layer = &World[priochan-1])->allocated)
if (r & layer->ptymask)
{ /* output to send to top MacLayers window */
len = read(layer->ptyfd, buf, IOSIZE);
if (len >= 0) /* if no error ... */
{ DO DEBUG("read output len %d chan %d\n", len, layer->chan);
}
else
{ /* We expect EIO error if socket not yet open on other end
** or if process using socket has terminated. We expect
** EWOULDBLOCK also after process terminates.
**/
DO DEBUG("read output err chan %d errno %d len %d\n",
layer->chan, errno, len);
if (errno == EIO || errno == EWOULDBLOCK)
DO DEBUG(" ...anticipated\n");
/* layer not ready or just terminated so wait-a-bit */
fderr |= layer->ptymask;
r &= ~layer->ptymask; /* don't read it again below */
errno = 0; /* clear errno */
}
if (len > 0)
SendData(layer->chan, buf, len);
if (len >= 0)
/* To keep lower priority channels from hogging the line
** we delay any output from them until the primary window
** has no more data to be sent to it.
*/
stall = 1; /* stall output from others */
}
/* now pass all available output to MacLayers */
if (!stall)
for (n=0; n<MAXPCHAN; n++)
if ((layer = &World[n])->allocated)
if (r & layer->ptymask)
{ /* output to send to MacLayers window */
len = read(layer->ptyfd, buf, IOSIZE);
if (len >= 0) /* if no error ... */
{ DO DEBUG("output chan %d len %d\n", layer->chan, len);
}
else
{ /* We expect EIO error if socket not yet open on other end
** or if process using socket has terminated. We expect
** EWOULDBLOCK also after process terminates.
**/
DO DEBUG("read output err chan %d errno %d len %d\n",
layer->chan, errno, len);
if (errno == EIO || errno == EWOULDBLOCK)
{ DO DEBUG(" ...anticipated\n");
}
/* layer not ready or just terminated so wait-a-bit */
fderr |= layer->ptymask;
errno = 0; /* clear errno */
}
if (len > 0)
SendData(layer->chan, buf, len);
}
/* handle signals again */
if (GotSignal)
SigHandler ();
} /* end while (1) */
/* NOT REACHED */
} /* main() */
/* ReceiveQuit() - MacLayers sends Quit packet */
void
ReceiveQuit()
{
/* We completely quit layers cancelling all active processes */
DO DEBUG("ReceiveQuit()\n");
FQuit(EXITNORMAL); /* normal termination */
/* NOT REACHED */
} /* ReceiveQuit() */
/* ReceiveNew() - MacLayers requests a new shell layer */
void
ReceiveNew(chanid, shape)
int chanid; /* channel for new shell layer */
struct Shape *shape; /* shape for new channel */
{
static int MakeWindow();
DO DEBUG("ReceiveNew(%d)\n", chanid);
(void) MakeWindow (chanid, *ShellArgs, ShellArgs,
(char *) 0, loginflag, shape);
} /* ReceiveNew() */
/* ReceiveDelete() - MacLayers has removed a layer */
void
ReceiveDelete(chanid)
int chanid; /* channel which was deleted */
{
static void KillWindow();
struct Layer *layer; /* layer pointer */
/* validate channel */
DO DEBUG("ReceiveDelete(%d)\n", chanid);
if (chanid <= 0 || chanid > MAXPCHAN)
return; /* ignore invalid channel */
/* if this layer active then kill it off, else ignore request */
layer = &World[chanid-1]; /* locate target layer */
if (layer->allocated)
KillWindow(layer);
} /* ReceiveDelete() */
/* ReceiveSignal() - send signal to layer's process group */
void
ReceiveSignal(chanid, signal)
int chanid; /* layer's channel */
int signal; /* signal.h signal ID */
{
struct Layer *layer; /* layer pointer */
DO DEBUG("ReceiveSignal(%d,%d)\n", chanid, signal);
/* verify channel */
if (chanid <= 0 || chanid > MAXPCHAN)
return; /* ignore invalid channel */
/* if this layer is active send the signal to the process group */
layer = &World[chanid-1]; /* locate target layer */
if (layer->allocated && layer->lpid)
KillPG(layer, signal);
} /* ReceiveSignal() */
/* ReceiveReshape() - windowsize and location updated */
void
ReceiveReshape(chanid, shape)
int chanid; /* channel having shape */
struct Shape *shape; /* shape structure */
{
struct Layer *layer; /* layer pointer */
DO DEBUG("ReceiveReshape(%d)\n", chanid);
/* verify channel */
if (chanid <= 0 || chanid > MAXPCHAN)
return; /* ignore invalid channel */
/* if this layer is active then reshape it's window */
layer = &World[chanid-1]; /* locate target layer */
if (layer->allocated && layer->lpid)
{ layer->shape = *shape; /* install as our new shape */
SetWindowSize(layer); /* udpate the O/S window info */
}
} /* ReceiveReshape() */
/* SetWindowSize() - tell O/S about new window size */
static void
SetWindowSize(layer)
struct Layer *layer; /* layer to resize */
{
#ifdef TIOCSWINSZ
struct winsize wsize; /* window size structure */
int retcode; /* ioctl return code */
wsize.ws_col = layer->shape.wchars; /* character width */
wsize.ws_row = layer->shape.wlines; /* window height */
wsize.ws_xpixel = 0; /* necessary? */
wsize.ws_ypixel = 0;
/* update O/S window state */
retcode = ioctl(layer->ptyfd, TIOCSWINSZ, &wsize);
DO DEBUG("SetWindowSize(chan %d) col %d, row %d iotcl() = %d\n",
layer->chan, layer->shape.wchars, layer->shape.wlines,
retcode);
retcode = ioctl(layer->ptyfd, TIOCGWINSZ, &wsize);
DO DEBUG("TIOCGWINSZ: col %d, row %d iotcl() = %d\n",
wsize.ws_col, wsize.ws_row, retcode);
#ifdef SIGWCHG
/* inform process group that control terminal has altered size */
KillPG(layer, SIGWCHG);
#endif
#endif
} /* SetWindowSize() */
/* ReceiveData() - received keyboard input for layer */
void
ReceiveData(chanid, buff, cnt)
int chanid; /* channel receiving input */
char *buff; /* buffer containing data */
int cnt; /* count of data bytes */
{
struct Layer *layer; /* layer pointer */
struct Kbuff *kb; /* keybuff pointer */
DO DEBUG("ReceiveData(%d, '%.*s')\n", chanid, cnt, buff);
/* verify channel */
if (chanid <= 0 || chanid > MAXPCHAN)
return; /* ignore invalid channel */
layer = &World[chanid-1]; /* locate target layer */
/* add character stream to layer's input buffers for main loop processing */
for (kb=layer->kbuff; kb->next; kb=kb->next); /* find oldest buffer */
while (cnt--)
{
/* if current buffer full then chain in a new one */
if (kb->offset+kb->size >= IOSIZE)
{ kb->next = (struct Kbuff *) malloc(sizeof(struct Kbuff));
kb = kb->next; /* base new keybuff */
kb->next = NULL; /* no next yet */
kb->size = 0; /* this character is first */
kb->offset = 0; /* at zero offset */
}
/* add new character to the end of this buffer */
kb->text[kb->offset+kb->size++] = *buff++; /* insert at end of data */
}
} /* ReceiveData() */
/* InitWorld() - initialize layer structures */
static void
InitWorld()
{
struct Layer *layer; /* layer pointer */
struct Kbuff *kb; /* keybuff pointer [UNUSED] */
int i; /* work variable */
for (i=0; i<MAXPCHAN; i++)
{ layer = &World[i]; /* point to layer */
layer->chan = i+1; /* channel ID */
layer->allocated = 0; /* does not exist yet */
layer->lpid = 0; /* head process */
layer->ptyfd = 0; /* no pseduo pty yet */
layer->ptymask = 0; /* no pty mask yet */
layer->slot = 0; /* no Utmp slot */
ClearShape(&layer->shape); /* clear shape structure */
/* allocate the primary input keybuffer for this layer */
layer->kbuff = (struct Kbuff *) malloc(sizeof(struct Kbuff));
layer->kbuff->next = NULL; /* no next buffer */
layer->kbuff->size = 0; /* no data in buffer */
layer->kbuff->offset = 0; /* start filling at front */
} /* end for layer scan */
} /* InitWorld() */
/* clearshape() - initialize shape structure */
static void
ClearShape(shape)
struct Shape *shape; /* shape structure pointer */
{
shape->worigv = 0; /* default window position */
shape->worigh = 0;
shape->wlines = 0; /* default size */
shape->wchars = 0;
shape->wfont = 0; /* default font size */
shape->wattr = 0; /* no attributes */
} /* clearshape() */
/* SigHandler() - process signals */
void
SigHandler ()
{
static void DoWait();
DO DEBUG("GotSignal()\n");
while (GotSignal)
{ GotSignal = 0;
DoWait (); /* handle dead or stopped children processes */
#ifdef SYSV
signal(SIGCLD, SigChld);
#endif
}
}
#ifdef SVR4
static void
SigChld(int sn, siginfo_t * si, void * uc)
{
int i;
struct Layer *layer;
if (si == NULL) {
DO DEBUG("SigChld: OOPS! no siginfo\n");
return;
}
DO DEBUG("code %d errno %d pid %d\n",
si->si_code, si->si_errno, si->si_pid);
if (si->si_code == CLD_STOPPED) {
(void) sigsend(P_PID, si->si_pid, SIGCONT);
return;
}
for (i = 0; i < MAXPCHAN; i++) {
if ((layer = &World[i])->lpid == si->si_pid) {
DO DEBUG("dead session reaped\n");
layer->lpid = (-layer->lpid); /* Mark as dead */
GotSignal = 1;
}
}
}
#else
static SIGTYPE
SigChld ()
{
DO DEBUG("SigChld()\n");
/* flag child process is stopped or dead */
GotSignal = 1;
}
#endif
static SIGTYPE
SigHup ()
{
DO DEBUG("SigHup()\n");
/* Detach (0); */
FQuit(EXITSIGHUP); /* stop all processes */
/* NOT REACHED */
}
/* DoWait() - send SIGCONT to stopped windows, Free dead process windows */
#ifdef SVR4
static void
DoWait()
{
static void KillWindow();
int i;
register struct Layer *layer;
DO DEBUG("DoWait()\n");
for (i = 0; i < MAXPCHAN; i++) {
if ((layer = &World[i])->lpid < 0) {
layer->lpid = (-layer->lpid);
DO DEBUG("kill dead process window %d\n", layer->chan);
KillWindow(layer);
SendDelete(layer->chan); /* tell MacLayers layer is dead */
}
}
} /* DoWait() */
#else
static void
DoWait()
{
static void KillWindow();
register pid;
register struct Layer *layer;
union wait wstat;
int i;
DO DEBUG("DoWait()\n");
while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0)
/* dead or stopped child process found */
for (i=0; i<MAXPCHAN; i++)
if ((layer = &World[i])->lpid == pid)
{ if (WIFSTOPPED (wstat))
{ /* stopped process so restart it */
/*** DO WE REALLY NEED TO DO THIS? ***/
DO DEBUG("killpg(, SIGCONT)\n");
KillPG(layer, SIGCONT);
}
else
{ /* remove dead process's layer */
DO DEBUG("kill dead process window %d\n", layer->chan);
KillWindow (layer);
/* tell MacLayers layer is dead */
SendDelete(layer->chan);
}
}
} /* DoWait() */
#endif
/* KillPG() - send signal to layer's process group */
static void
KillPG(layer, signal)
struct Layer *layer; /* layer to signal */
int signal; /* signal to send */
{
int retcode; /* work variable */
int pgrp; /* process group for layer */
int tgrp; /* terminal control process group */
DO DEBUG("KillPG(%d, sig %d)\n", layer->chan, signal);
if (layer->lpid) {
#ifdef SVR4
pgrp = getpgid (layer->lpid); /* get process group */
DO DEBUG("getpgid() = %d\n", pgrp);
if (pgrp != -1) {
retcode = sigsend(P_PGID, pgrp, signal); /* signal it */
DO DEBUG("sigsend() = %d\n", retcode);
}
#else
#if defined(AUX) || defined(AIX)
/*
* getpgrp does not get anyone else's pgrp on A/UX. You could
* find this by mucking in the kernel, but I'm not willing to
* go that far yet.
*/
pgrp = layer->lpid;
#else
pgrp = getpgrp(layer->lpid); /* get process group */
DO DEBUG("getpgrp() = %d\n", pgrp);
if (pgrp != -1)
{ retcode = killpg(pgrp, signal); /* signal it */
DO DEBUG("killpg() = %d\n", retcode);
}
/*
** In order to accomplish job control, csh and derived shells
** create a new terminal control group whenever they run a command.
** Thus the above code failed to forward SIGINT to such commands.
** (Csh would get it but just ignore it.) Thus the following code.
*/
/* forward SIGINT to the terminal control group also */
/* (This should probably be done for ALL Signals, but at present is not) */
if (signal == SIGINT)
{ retcode = ioctl(layer->ptyfd, TIOCGPGRP, &tgrp);
DO DEBUG("ioctl(ptyfd,TIOCGPGRP) termcntlgrp %d, retcode = %d\n",
tgrp, retcode);
/* but only if not the same process group */
if (retcode != -1 && tgrp != pgrp)
{ retcode = killpg(tgrp, signal); /* signal it */
DO DEBUG("killpg(%d) = %d\n", tgrp, retcode);
}
}
#endif
#endif
}
} /* KillPG() */
/* KillWindow() - remove a layer from the system */
/* Note: This function does NOT tell MacLayers about the dead layer */
static void
KillWindow (layer)
struct Layer *layer;
{
struct Kbuff *kb; /* work buffer free pointer */
if (layer->allocated)
{ /* SHOULD THIS BE SIGKILL or SIGHUP ??? */
if (layer->lpid) /* if layer process started ... */
{ KillPG(layer, SIGHUP); /* kill processes */
layer->lpid = 0; /* clear pid field */
}
RemoveTtyFromUtmp(layer);
(void) chmod(layer->tty, 0666);
(void) chown(layer->tty, 0, 0);
close(layer->ptyfd);
DO DEBUG("chmod/chown %s, SendDelete(%d)\n",layer->tty, layer->chan);
ClearShape(&layer->shape); /* reset the shape structure */
/* free all keybuffers but the last one and reprime it */
for (kb=layer->kbuff; kb->next; kb=layer->kbuff) {
layer->kbuff = kb->next;
free(kb); /* free input buffers */
}
kb->size = 0; /* empty buffer */
kb->offset = 0; /* start refill from front */
layer->allocated = 0; /* window no longer allocated */
}
} /* KillWindow() */
/* FAbort() - signal catcher for quitting */
static SIGTYPE
FAbort()
{
DO DEBUG("FAbort()\n");
FQuit (EXITSIGQUIT); /* quit with error exit */
} /* FAbort() */
/* FQuit() - terminate layers */
void
FQuit(exitcode)
int exitcode;
{
int i;
#ifndef POSIXTTY
static void SetTTY();
#endif
DO DEBUG("FQuit(%d)\n",exitcode);
for (i=0; i<MAXPCHAN; i++)
KillWindow(&World[i]); /* kill all windows */
DO DEBUG("SendQuit()\n");
SendQuit(); /* tell MacLayers to exit layers mode */
SetTTY (0, &OldMode);
if (RealTtyMode)
(void) chmod(RealTtyName, RealTtyMode); /* restore mode */
RestoreRealUtmp();
FinitTerm ();
sleep(2); /* wait for port to reset */
fputs ("[layers terminated]\n", stdout);
exit (exitcode);
} /* FQuit() */
/* MakeWindow() - create new layer */
static int
MakeWindow (chan, prog, args, dir, lflag, shape)
int chan; /* zero or channel to use for window */
char *prog;
char **args;
char *dir;
int lflag; /* one if this to be logged in */
struct Shape *shape; /* shape to use for window */
{
void Msg();
static void SendErrorMsg(), execvpe();
#ifndef POSIXTTY
static void SetTTY();
#endif
static int OpenPTY();
register struct Layer *layer;
register char **cp; /* [UNUSED] */
register f;
int tf;
int mypid;
char ebuf[10];
DO DEBUG("MakeWindow(%d, %s, %s, dir %s, ",
chan, prog, args[0], dir ? dir : "(none)");
DO DEBUG("login %d\n", lflag);
DO DEBUG(" origv %d, origh %d, lines %d, chars %d, ",
shape->worigv, shape->worigh, shape->wlines, shape->wchars);
DO DEBUG("font %d, attr 0x%x\n", shape->wfont, shape->wattr);
if ((f = OpenPTY ()) == -1)
{ Msg (0, "No more PTYs.");
return ( -1 );
}
/*
* If channel not given,
* obtain one from MacLayers:
*/
if (chan == 0)
{ chan = SendNew(shape); /* try to get free window */
if (chan == 0)
{ Msg (0, "No more windows.");
return ( -1 );
}
DO DEBUG("SendNew() == %d\n", chan);
}
/* verify channel */
if (chan <= 0 || chan > MAXPCHAN)
{ Msg(0, "Invalid channel %d.", chan);
return ( -1 );
}
/* login this window if it's layer #1 */
if (chan == 1)
lflag = 1;
if (lflag == -1)
lflag = loginflag;
#ifdef USRLIMIT
/*
* Count current number of users, and if logging windows in,
*/
if (lflag == 1)
{ (void) lseek (utmpf, 0, 0);
UserCount = 0;
while (read(utmpf, &utmpbuf, sizeof(struct utmp)) > 0)
{ if (utmpbuf.ut_name[0] != '\0')
UserCount++;
}
if (UserCount >= USRLIMIT)
{ /* MUST NOT ISSUE MESSAGE SINCE IT GETS INTERSPERSED WITH
** PACKET SENT INDICATING CURRENT INPUT WINDOW!! THIS
** CAUSES IT TO BE CORRUPTED. (1.00 Bug Fix 001)
*/
/* Msg (0, "User limit reached. Window will not be logged in."); */
lflag = 0;
}
}
#endif USRLIMIT
layer = &World[chan-1]; /* find layer structure */
layer->shape = *shape; /* install new window shape */
/* ??? What do we do if layer is still active as far as we're concerned? */
if (layer->allocated)
{ DO DEBUG("??? newlayer not free !!!\n");
KillWindow(layer); /* kill off old layer */
SendDelete(layer->chan); /* kill window back off */
Msg (0, "Makewindow error: Duplicate active layer %d.", chan);
return ( -1 ); /* return failed */
}
layer->allocated = 1; /* show layer now in use */
BuildTitle(chan, prog, args); /* install window title */
#ifdef SYSV
(void) fcntl(f, F_SETFL, O_NDELAY);
#else
(void) fcntl(f, F_SETFL, FNDELAY);
#endif
layer->ptyfd = f; /* pseudo pty for task's I/O */
layer->ptymask = 1<<f; /* set pty device mask */
strncpy (layer->cmd, Filename (args[0]), MAXSTR-1);
layer->cmd[MAXSTR-1] = '\0';
strncpy (layer->tty, TtyName, MAXSTR-1);
DO DEBUG("forking %s, tty %s, ptyfd %d, mask %x\n",
layer->cmd, layer->tty, layer->ptyfd, layer->ptymask);
(void) chown (TtyName, getuid (), getgid ());
if (lflag == 1) {
layer->slot = SetUtmp(TtyName, chan == 1, World[0].lpid);
if (chan == 1 && RealTtyMode)
/* set to original tty umask */
(void) chmod(TtyName, RealTtyMode);
else
/* force to this mode */
(void) chmod(TtyName, TtyMode);
}
else
{ layer->slot = -1;
/* do not allow any other user access to this device */
(void) chmod (TtyName, 0600);
}
switch (layer->lpid = fork ())
{ case -1:
Msg (errno, "fork");
layer->lpid = 0; /* clear pid field */
return ( -1 ); /* return failed */
case 0:
signal (SIGHUP, SIG_DFL);
signal (SIGINT, SIG_DFL);
signal (SIGQUIT, SIG_DFL);
signal (SIGTERM, SIG_DFL);
signal (SIGTTIN, SIG_DFL);
signal (SIGTTOU, SIG_DFL);
signal (SIGALRM, SIG_DFL);
setuid (getuid ());
setgid (getgid ());
if (dir && chdir (dir) == -1)
{ SendErrorMsg ("Cannot chdir to %s: %s", dir, sys_errlist[errno]);
exit (EXITABNORMAL);
}
#if defined(SVR4) || defined(AUX) || defined(AIX)
mypid = setsid();
#else
mypid = getpid ();
(void) setpgrp (0, mypid);
ioctl (DevTty, TIOCNOTTY, (char *)0);
#endif
if ((tf = open (TtyName, O_RDWR)) == -1)
{ SendErrorMsg ("Cannot open %s: %s", TtyName, sys_errlist[errno]);
exit (EXITABNORMAL);
}
#ifdef SVR4
(void) ioctl(tf, I_PUSH, "ptem");
(void) ioctl(tf, I_PUSH, "ldterm");
if(mypid > 0 && tcsetpgrp(tf, mypid) < 0) {
DO DEBUG("tcsetpgrp(): %s\n", strerror(errno));
}
#endif
DO DEBUG("Now in new process\n");
fflush(stdout);
fflush(stderr);
(void) dup2 (tf, 0);
(void) dup2 (tf, 1);
(void) dup2 (tf, 2);
for (f = getdtablesize () - 1; f > 2; f--)
close (f);
ioctl (0, TIOCSPGRP, &mypid);
SetTTY (0, &OldMode);
{ struct winsize wsize; /* window size structure */
int retcode; /* ioctl return code */
wsize.ws_col = layer->shape.wchars; /* character width */
wsize.ws_row = layer->shape.wlines; /* window height */
wsize.ws_xpixel = 0; /* necessary? */
wsize.ws_ypixel = 0;
/* update O/S window state */
retcode = ioctl(0, TIOCSWINSZ, &wsize);
}
(void) umask(Oumask); /* restore user's original umask */
NewEnv[2] = MakeTermcap(layer->shape.wlines, layer->shape.wchars);
sprintf (ebuf, "LAYER=%d", chan);
NewEnv[3] = ebuf;
execvpe (prog, args, NewEnv);
printf("%s: cannot exec %s: %s", myname, prog, sys_errlist[errno]);
exit (EXITABNORMAL);
}
DO DEBUG("MakeWindow() returning channel %d\n", chan);
return ( chan );
} /* MakeWindow() */
static void
execvpe (prog, args, env)
char *prog, **args, **env;
{
register char *path, *p;
char buf[1024];
char *shargs[MAXARGS+1];
register i;
register eaccess = 0;
if (prog[0] == '/')
path = "";
else
if ((path = getenv ("PATH")) == 0)
path = DefaultPath;
do
{ p = buf;
while (*path && *path != ':')
*p++ = *path++;
if (p > buf)
*p++ = '/';
strcpy (p, prog);
if (*path)
++path;
execve (buf, args, env);
switch (errno)
{ case ENOEXEC:
shargs[0] = DefaultShell;
shargs[1] = buf;
for (i = 1; shargs[i+1] = args[i]; ++i);
execve (DefaultShell, shargs, env);
return;
case EACCES:
eaccess = 1;
break;
case ENOMEM: case E2BIG: case ETXTBSY:
return;
} /* end switch */
} while (*path);
if (eaccess)
errno = EACCES;
} /* execvpe() */
/* BuildTitle() - create and install window title */
static void
BuildTitle(chan, prog, args)
int chan; /* channel for title */
char *prog; /* program being executed */
char **args; /* arg list */
{
int i; /* arg scan index */
char buff[1024]; /* super huge title buffer */
/* skip any leading "/bin/" */
if (strncmp(prog, "/bin/", 5) == 0)
strcpy(buff, prog+5); /* leave /bin off */
else if (strncmp(prog, "/usr/bin/", 9) == 0)
strcpy(buff, prog+9); /* leave /usr/bin off */
else
strcpy(buff, prog); /* start with program name */
/* add all aguments but stop if option ("-") seen */
for (i=1; args[i] && args[i][0] != '-'; i++)
{ strcat(buff, " "); /* delimiter */
strcat(buff, args[i]); /* add next parameter */
}
SendTitle(chan, buff, strlen(buff)); /* set new window title */
} /* BuildTitle() */
#ifdef sequent
static int
OpenPTY ()
{
char *m, *s;
register f;
f = getpseudotty (&s, &m);
strncpy (PtyName, m, sizeof (PtyName));
strncpy (TtyName, s, sizeof (TtyName));
ioctl (f, TIOCFLUSH, (char *)0);
return (f);
}
#else
#ifdef IRIX
static
OpenPTY ()
{
int f;
char *tn;
if ((tn = _getpty(&f, O_RDWR, TtyMode, 0)) != 0) {
strncpy(TtyName, tn, sizeof (TtyName));
return f;
} else
return -1;
}
#else
#ifdef SVR4
static int
OpenPTY()
{
extern char *ptsname();
int fdm;
if ((fdm = open("/dev/ptmx", O_RDWR)) < 0)
return (-1);
grantpt(fdm);
unlockpt(fdm);
(void) strcpy(TtyName, ptsname(fdm));
return fdm;
}
#else
static
OpenPTY ()
{
register char *p, *l, *d;
register i, f, tf;
strcpy (PtyName, PtyProto);
strcpy (TtyName, TtyProto);
for (p = PtyName, i = 0; *p != 'X'; ++p, ++i);
#ifndef SEQUENT
for (l = "pqrstuvwxyzPQRST"; *p = *l; ++l)
{ for (d = "0123456789abcdef"; p[1] = *d; ++d)
#else
for (l = "pqrstuvwPQRSTUVW"; *p = *l; ++l)
{ for (d = "0123456789abcdef"; p[1] = *d; ++d)
#endif
{ if ((f = open (PtyName, O_RDWR)) != -1)
{ TtyName[i] = p[0];
TtyName[i+1] = p[1];
#ifndef AUX
if ((tf = open (TtyName, O_RDWR)) != -1)
{ close (tf);
return f;
}
#else
/*
* On A/UX, opening and closing the slave pty will result
* in the master getting nothing but EIO. Thus, we delay
* opening the slave pty until we're really ready for it.
*/
return f;
#endif
close (f);
}
}
}
return -1;
} /* OpenPTY() */
#endif /* NOT SVR4 */
#endif /* NOT IRIX */
#endif /* NOT sequent */
#ifdef POSIXTTY
static
SetMode(op, np)
struct termios *op, *np;
{
*np = *op;
if (flowctl)
np->c_iflag = BRKINT|IGNPAR|IXON|IXOFF;
else
np->c_iflag = BRKINT|IGNPAR;
np->c_oflag = 0; /* No special output processing */
/* leave cflag alone, it has the baud rate etc. */
np->c_lflag = 0; /* should put terminal in raw mode ... */
np->c_cc[VMIN] = 1;
np->c_cc[VTIME] = 0;
}
#else
#ifdef USE_TERMIO
static void
SetTTY (fd, mp)
struct termio *mp;
{
ioctl (fd, TCSETAF, mp);
} /* SetTTY() */
static void
GetTTY (fd, mp)
struct termio *mp;
{
ioctl (fd, TCGETA, mp);
} /* GetTTY() */
static
SetMode (op, np)
struct termio *op, *np;
{
*np = *op;
np->c_iflag = 0; /* no input processing */
np->c_oflag &= ~OPOST; /* no output processing */
np->c_lflag = NOFLSH; /* don't flush I/O on quit/swtch/intrpt */
np->c_cc[VMIN] = 1; /* read ends of this many chars */
np->c_cc[VTIME] = 0; /* or this many .1 seconds after char recieved */
} /* SetMode() */
#else
static void
SetTTY (fd, mp)
int fd;
struct mode *mp;
{
ioctl (fd, TIOCSETP, &mp->m_ttyb);
ioctl (fd, TIOCSETC, &mp->m_tchars);
ioctl (fd, TIOCSLTC, &mp->m_ltchars);
ioctl (fd, TIOCLSET, &mp->m_lmode);
ioctl (fd, TIOCSETD, &mp->m_ldisc);
} /* SetTTY() */
static void
GetTTY (fd, mp)
int fd;
struct mode *mp;
{
ioctl (fd, TIOCGETP, &mp->m_ttyb);
ioctl (fd, TIOCGETC, &mp->m_tchars);
ioctl (fd, TIOCGLTC, &mp->m_ltchars);
ioctl (fd, TIOCLGET, &mp->m_lmode);
ioctl (fd, TIOCGETD, &mp->m_ldisc);
} /* GetTTY() */
static
SetMode (op, np)
struct mode *op, *np;
{
*np = *op;
#if 1
if (flowctl)
{ np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
np->m_ttyb.sg_flags |= CBREAK | ANYP;
}
else
np->m_ttyb.sg_flags = RAW | ANYP;
#else
np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
np->m_ttyb.sg_flags |= CBREAK | ANYP;
#endif
np->m_tchars.t_intrc = -1;
np->m_tchars.t_quitc = -1;
if (!flowctl)
{ np->m_tchars.t_startc = -1;
np->m_tchars.t_stopc = -1;
}
np->m_ltchars.t_suspc = -1;
np->m_ltchars.t_dsuspc = -1;
np->m_ltchars.t_flushc = -1;
np->m_ltchars.t_lnextc = -1;
} /* SetMode() */
#endif /* USE_TERMIO */
#endif
static char *
GetTtyName ()
{
void Msg();
int n;
char *p;
for (p = 0, n = 0; n <= 2 && !(p = ttyname (n)); n++);
if (!p || *p == '\0')
Msg (0, "layers must run on a tty.");
return ( p );
} /* GetTtyName() */
static void
Kill (pid, sig) /* [UNUSED] */
{
if (pid != 0)
(void) kill (pid, sig);
}
/* GetSockName() - set SockName; if LTY env return 1 else 0 */
static int
GetSockName ()
{
register int client;
static char buf[2*MAXSTR];
if (!mflag && (SockName = getenv ("LTY")) != 0 && *SockName != '\0')
{ client = 1;
setuid (getuid ());
setgid (getgid ());
}
else
{ sprintf (buf, "%s.%s", HostName, Filename (RealTtyName));
SockName = buf;
client = 0;
}
return client;
} /* GetSockName() */
static int
MakeServerSocket ()
{
void Msg();
register int s;
struct sockaddr_un a;
char *p;
if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
Msg (errno, "socket");
a.sun_family = AF_UNIX;
strcpy (SockNamePtr, SockName);
strcpy (a.sun_path, SockPath);
if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) != -1)
{ p = Filename (SockPath);
Msg (0, "You already have a layers running on %s.", p);
/*NOTREACHED*/
}
DO DEBUG("MakeServerSocket: unlink(SockPath)/bind()/chown/listen\n");
(void) unlink (SockPath);
if (bind (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
Msg (errno, "bind");
(void) chown (SockPath, getuid (), getgid ());
if (listen (s, 5) == -1)
Msg (errno, "listen");
return s;
} /* MakeServerSocket() */
static int
MakeClientSocket (err)
int err;
{
void Msg();
register int s;
struct sockaddr_un a;
if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
Msg (errno, "socket");
a.sun_family = AF_UNIX;
strcpy (SockNamePtr, SockName);
strcpy (a.sun_path, SockPath);
if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
{ if (err)
{ Msg (errno, "connect: %s", SockPath); }
else
{ close (s);
return -1;
}
}
return s;
} /* MakeClientSocket() */
static void
SendCreateMsg (s, ac, av, lflag, shape)
int s, ac, lflag;
char **av;
struct Shape *shape;
{
void Msg();
struct msg m;
register char *p;
register len, n;
char *pwd; /* PWD environment string */
DO DEBUG("SendCreateMsg(%d, ac %d, lflag %d\n", s, ac, lflag);
m.type = MSG_CREATE;
p = m.m.create.line;
for (n = 0; ac > 0 && n < MAXARGS-1; ++av, --ac, ++n)
{ len = strlen (*av) + 1;
if (p + len >= m.m.create.line+MAXLINE)
break;
strcpy (p, *av);
p += len;
}
DO DEBUG(" nargs %d, create line = '%s'\n", n, m.m.create.line);
m.m.create.nargs = n;
m.m.create.lflag = lflag;
m.m.create.shape = *shape; /* pass window shape */
/* Since Suns can hang up on getwd() [damn their stupid networking]
** we try to get the current working directory first from the PWD
** environment variable.
*/
if ((pwd=getenv("PWD")) && strlen(pwd) < 1024)
(void) strcpy(m.m.create.dir, pwd);
else
#ifdef SVR4
if (getcwd (m.m.create.dir, sizeof(m.m.create.dir)) == NULL) {
DO DEBUG("getcwd() failed!!\n");
Msg (0, "%s", strerror(errno));
}
#else
if (getwd (m.m.create.dir) == 0)
{ DO DEBUG("getwd() failed!!\n");
Msg (0, "%s", m.m.create.dir);
}
#endif
DO DEBUG(" create.dir = '%s'\n", m.m.create.dir);
if (write (s, (char *)&m, sizeof (m)) != sizeof (m))
{ DO DEBUG(" write failed!!\n");
Msg (errno, "write");
}
DO DEBUG("SendCreateMsg() done\n");
} /* SendCreateMsg() */
/*VARARGS1*/
static void
SendErrorMsg (fmt, p1, p2, p3, p4, p5, p6)
char *fmt;
{
register s;
struct msg m;
s = MakeClientSocket (1);
m.type = MSG_ERROR;
sprintf (m.m.message, fmt, p1, p2, p3, p4, p5, p6);
(void) write (s, (char *)&m, sizeof (m));
close (s);
sleep (2);
}
static void
ReceiveMsg (s)
int s;
{
void Msg();
static void ExecCreate();
register ns;
struct sockaddr_un a;
int left, len = sizeof (a);
struct msg m;
char *p;
DO DEBUG ("ReceiveMsg()\n");
if ((ns = accept (s, (struct sockaddr *)&a, &len)) == -1)
{ Msg (errno, "accept");
return;
}
p = (char *)&m;
left = sizeof (m);
while (left > 0 && (len = read (ns, p, left)) > 0)
{ p += len;
left -= len;
}
close (ns);
if (len == -1)
Msg (errno, "read");
if (left > 0)
return;
switch (m.type)
{ case MSG_CREATE:
DO DEBUG("MSG_CREATE:\n");
ExecCreate (&m);
break;
case MSG_ERROR:
DO DEBUG("MSG_ERROR:\n");
Msg (0, "%s", m.m.message);
break;
default:
Msg (0, "Invalid message (type %d).", m.type);
} /* end switch */
} /* ReceiveMsg() */
static void
ExecCreate (mp)
struct msg *mp;
{
char *args[MAXARGS];
register n;
register char **pp = args, *p = mp->m.create.line;
for (n = mp->m.create.nargs; n > 0; --n)
{ *pp++ = p;
p += strlen (p) + 1;
}
*pp = 0;
n = MakeWindow (0, mp->m.create.line, args, mp->m.create.dir,
mp->m.create.lflag, &mp->m.create.shape);
} /* ExecCreate() */
/*
* manifest constants for processing .layersrc file:
*/
#define RC_NONE 0
#define RC_UNKNOWN 1
#define RC_WINDOW 2
/*
* ProcessGeo():
*
* expects passed string to be of the form
*
* ###x### indicating width and height
* or
* ###x###+###+### indicating width, height, absolute (x,y) position
*
* parses string and sets passed shape accordingly
*
* returns nonzero if string could not be parsed
*/
static
int ProcessGeo(shape, p)
struct Shape *shape;
char *p;
{
char buf[256];
int i;
int w, h, x, y;
/* get width: */
i = 0;
while (isdigit(*p))
buf[i++] = *p++;
if (*p != 'x')
return -1; /* parse error */
buf[i] = '\0';
sscanf(buf, "%d", &w); /* parse width */
/* get height: */
i = 0; p++;
while (isdigit(*p))
buf[i++] = *p++;
if ((*p) && (*p != '+') && (*p != '-'))
return -1; /* parse error */
buf[i] = '\0';
sscanf(buf, "%d", &h); /* parse height */
DO DEBUG("ProcessGeo(): %d %d\n", w, h);
shape->wchars = w;
shape->wlines = h;
if (!(*p))
return 0; /* width x height parsed without errors */
buf[i=0] = *p++;
i++;
while (isdigit(*p))
buf[i++] = *p++;
if ((*p != '+') && (*p != '-'))
return -1; /* parse error */
buf[i] = '\0';
sscanf(buf, "%d", &x); /* parse x-position */
buf[i=0] = *p++;
i++;
while (isdigit(*p))
buf[i++] = *p++;
/* should be end of argument: */
if (*p)
return -1; /* parse error */
buf[i] = '\0';
sscanf(buf, "%d", &y); /* parse y-position */
shape->worigh = x;
shape->worigv = y;
DO DEBUG("ProcessGeo(): %d %d %d %d\n", w, h, x, y);
return 0; /* width x height x horiz x vert */
}
static
int InterpretLayerArgs(argc, ap)
int argc;
char *ap[];
{
char *progargs[MAXARGS];
char *prog;
struct Shape shape; /* shape for new window */
char *title = NULL;
int login = 0; /* login layer */
int retval = 0,
i=0;
ClearShape(&shape); /* initialize shape */
progargs[0] = prog = ShellProg;
progargs[1] = NULL;
while (++i < argc) {
if ( (!strcmp("-login", ap[i])) || (!strcmp("-l", ap[i])) )
login = 1;
else if ( (!strcmp("-geometry", ap[i])) || (!strcmp("-g", ap[i])) ) {
if (i+1 < argc) {
i++;
if (ProcessGeo(&shape, ap[i]))
DO DEBUG("InterpretLayerArgs(): error while parsing -geometry argument\n");
} else
DO DEBUG("InterpretLayerArgs(): %s missing argument\n", ap[i]);
} else if ( (!strcmp("-exec", ap[i])) || (!strcmp("-e", ap[i])) ) {
if (i+1 < argc) {
int j=0;
i++;
do {
progargs[j++] = ap[i];
} while (++i < argc);
prog = progargs[0];
progargs[j] = NULL;
} else
DO DEBUG("InterpretLayerArgs(): %s missing argument\n", ap[i]);
} else if ( (!strcmp("-title", ap[i])) || (!strcmp("-t", ap[i])) ) {
if (i+1 < argc)
title = ap[++i];
else
DO DEBUG("InterpretLayerArgs(): %s missing argument\n", ap[i]);
}
}
shape.wattr |= Wa_shell; /* indicate a shell window */
if ((i=MakeWindow(0, prog, progargs, home, login, &shape)) < 0) {
DO DEBUG("InterpretLayerArgs(): MakeWindow() unsuccessful (%d)\n", i);
} else {
if (title) SendTitle(i, title, strlen(title));
retval = 1;
}
return retval;
}
static
int ReadRc (fn)
char *fn;
{
static int Parse();
FILE *f;
register char *p, **ap;
register argc;
char buf[256];
char *args[MAXARGS];
int rcmode; /* type of entry */
int l = 0, /* line within file */
i;
int numwin = 0; /* number of successfully */
ap = args;
/* insure file can be accessed: */
if (access (fn, R_OK) == -1) {
DO DEBUG("ReadRc(): cannot access %s\n", fn);
return 0;
}
/* open file for reading: */
if ((f = fopen (fn, "r")) == NULL) {
DO DEBUG("ReadRc(): cannot open %s\n", fn);
return 0;
}
/* for each line in file: */
while (fgets (buf, 256, f) != NULL) {
l++;
/* terminate string with '\0': */
if (p = index (buf, '\n'))
*p = '\0';
argc = Parse (fn, buf, ap);
if (Dflag) for (i=0; i<argc; i++)
DO DEBUG("ReadRc(): line %d, token %d: \"%s\"\n",
l, i, ap[i]);
rcmode = RC_NONE;
if ((argc == 0) || (!strcmp(ap[0], "#")))
/* ignore */;
else if (!strcmp(ap[0], "layer"))
rcmode = RC_WINDOW;
else
rcmode = RC_UNKNOWN;
i = 0;
switch (rcmode) {
case RC_UNKNOWN :
DO DEBUG("ReadRc(): unknown keyword (%s)\n", ap[0]);
break;
case RC_WINDOW : {
numwin += InterpretLayerArgs(argc, ap);
}
break;
default :
/* ignore blank lines and comments */
break;
}
}
(void) fclose (f);
return numwin;
} /* ReadRc() */
static int
Parse (fn, buf, args)
char *fn, *buf, **args;
{
void Msg();
register char *p, **ap;
register delim, argc;
p = buf;
ap = args;
argc = 0;
for (;;)
{ while (*p && (*p == ' ' || *p == '\t'))
++p;
if (*p == '\0' || *p == '#')
return argc;
if (argc > MAXARGS-1)
Msg (0, "%s: too many tokens.", fn);
delim = 0;
if (*p == '"' || *p == '\'')
{ delim = *p; *p = '\0'; ++p; }
++argc;
*ap = p; ++ap;
while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
++p;
if (*p == '\0')
{ if (delim)
Msg (0, "%s: Missing quote.", fn);
else
return argc;
}
*p++ = '\0';
}
} /* Parse() */
static char **
SaveArgs (argc, argv) /* [UNUSED] */
register argc;
register char **argv;
{
void Msg();
register char **ap, **pp;
if ((pp = ap = (char **)malloc ((argc+1) * sizeof (char **))) == 0)
Msg (0, "Out of memory.");
while (argc--)
{ if ((*pp = malloc (strlen (*argv)+1)) == 0)
Msg (0, "Out of memory.");
strcpy (*pp, *argv);
++pp; ++argv;
}
*pp = 0;
return ap;
} /* SaveArgs() */
static void
MakeNewEnv ()
{
static int IsSymbol();
register char **op, **np = NewEnv;
static char buf[MAXSTR];
if ((int) strlen (SockName) > MAXSTR-5)
SockName = "?";
sprintf (buf, "LTY=%s", SockName);
*np++ = buf;
*np++ = Term;
np += 2;
for (op = environ; *op; ++op)
{ if (np == NewEnv + MAXARGS - 1)
break;
if ( !IsSymbol (*op, "TERM")
&& !IsSymbol (*op, "TERMCAP")
&& !IsSymbol (*op, "LTY")
)
*np++ = *op;
}
*np = 0;
} /* MakeNewEnv() */
static int
IsSymbol (e, s)
register char *e, *s;
{
register char *p;
register n;
for (p = e; *p && *p != '='; ++p);
if (*p)
{ *p = '\0';
n = strcmp (e, s);
*p = '=';
return n == 0;
}
return 0;
} /* IsSymbol() */
/*VARARGS2*/
void
Msg (err, fmt, p1, p2, p3, p4, p5, p6)
int err;
char *fmt;
{
char buf[1024];
register char *p = buf;
sprintf (p, fmt, p1, p2, p3, p4, p5, p6);
if (err) {
p += strlen (p);
if (err > 0 && err < sys_nerr)
sprintf (p, ": %s", sys_errlist[err]);
else
sprintf (p, ": Error %d", err);
}
if (!Abortonmsg) {
/* MakeStatus (buf, curr);*/
printf("%s\r\n", buf);
fflush(stdout);
} else {
printf ("%s\r\n", buf);
fflush(stdout);
if (Resetenvonmsg) {
SetTTY (0, &OldMode);
if (RealTtyMode)
(void) chmod(RealTtyName, RealTtyMode); /* restore mode */
RestoreRealUtmp();
FinitTerm ();
}
exit(exitcode);
}
} /* Msg() */
static char *
Filename (s)
char *s;
{
register char *p;
p = s + strlen (s) - 1;
while (p >= s && *p != '/')
--p;
return ++p;
} /* Filename() */
static int
IsNum (s, base) /* [UNUSED] */
register char *s;
register base;
{
for (base += '0'; *s; ++s)
if (*s < '0' || *s > base)
return 0;
return 1;
} /* IsNum() */
#ifndef GETTTYENT
static
setttyent ()
{
struct stat s;
register f;
register char *p, *ep;
if (ttnext)
{ ttnext = tt;
return;
}
if ((f = open (ttys, O_RDONLY)) == -1 || fstat (f, &s) == -1)
Msg (errno, ttys);
if ((tt = malloc (s.st_size + 1)) == 0)
Msg (0, "Out of memory.");
if (read (f, tt, s.st_size) != s.st_size)
Msg (errno, ttys);
close (f);
for (p = tt, ep = p + s.st_size; p < ep; ++p)
if (*p == '\n')
*p = '\0';
*p = '\0';
ttnext = tt;
} /* setttyent() */
static struct ttyent *
getttyent ()
{
static struct ttyent t;
if (*ttnext == '\0')
return NULL;
t.ty_name = ttnext + 2;
ttnext += strlen (ttnext) + 1;
return &t;
} /* getttyend() */
#endif
/* FinitTerm() - reset vt100 terminal */
static void
FinitTerm ()
{
/* print out termcap 'is' string to reset terminal */
#if 0
/* This string sets scroll region 1-24 and puts cursor at bottom line */
puts("\033[1;24r\033[24;1H", stdout);
#endif
fflush(stdout);
}
static void
AddCap (s)
char *s;
{
register n;
if (tcLineLen + (n = strlen (s)) > 55)
{ strcat (Termcap, "\\\n\t:");
tcLineLen = 0;
}
strcat (Termcap, s);
tcLineLen += n;
}
static char *
MakeTermcap(lines, chars)
int lines; /* default window lines */
int chars; /* default window chars */
{
char buf[1024];
strcpy(Termcap, TermcapConst1); /* start TERMCAP build */
strcat(Termcap, UserTerm); /* fill in User's terminal type */
strcat(Termcap, TermcapConst3); /* finish our own definition */
/* add more capabilities depending upon client version */
if (Protocollevel > 1)
strcat(Termcap, TermcapConst101);
if (lines <= 0 || lines > 200)
lines = rows; /* force default if none or invalid */
if (chars <= 0 || chars > 300)
chars = cols; /* force default if none or invalid */
sprintf(buf, "li#%d:co#%d:", lines, chars);
AddCap(buf);
return ( Termcap );
} /* MakeTermcap() */
/* DEBUG() - dump output routine */
void
DEBUG(format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
char *format;
int arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8;
{
fprintf(stderr, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
}
#ifdef SVR4
static struct utmpx RealUtmp; /* Real tty logon utmpx entry */
#else
static struct utmp RealUtmp; /* Real tty logon utmp entry */
#endif
/*
* Utmp functions for SYSV and Sun BSD:
* (Note: Appropriate utmpx functions for SVR4 follow)
*
* InitUtmp() - Zero out main utmp entry for controlling tty
* and save old value.
* SetUtmp() - Add a tty to utmp, "login" a new terminal
* RemoveTtyFromUtmp() - "logout" a terminal
* RestoreRealUtmp() - Restore original utmp entry for controlling tty
*/
#ifdef SYSVUTMP
static char UtmpName[] = "/etc/utmp";
#ifdef AUX
static char WtmpName[] = "/etc/wtmp";
#endif
static int RealSlot = 0; /* Real tty logon slot */
static void ut_tty_cpy(struct utmp *up, char * tty)
{
strncpy(up->ut_line, Filename(tty), sizeof up->ut_line);
up->ut_line[(sizeof up->ut_line) - 1] = '\0';
}
static void
ReadUtmp(int slot, struct utmp *entry)
{
int i;
struct utmp *ret;
setutent();
for (i=0; i<slot; i++) /* read and discard ... */
(void) getutent();
ret = getutent(); /* ... until we get to our entry */
*entry = *ret;
DO DEBUG("ReadUtmp slot %d errno %d, line %.8s, name %.8s\n",
slot, errno, entry->ut_line, entry->ut_name);
} /* ReadUtmp() */
static void
RemoveUtmp(int slot)
{
struct utmp u;
#ifdef AUX
int wtmpfd;
#endif
if (slot) {
setutent();
ReadUtmp(slot, &u);
u.ut_type = DEAD_PROCESS;
pututline(&u);
#ifdef AUX
if ((wtmpfd = open(WtmpName,1)) >= 0) {
lseek (wtmpfd, 0L, 2);
write (wtmpfd, (char *)&u, sizeof(u));
close (wtmpfd);
}
#endif
}
}
static int
FindUtmp(char *name)
{
register char *p;
register struct utmp *u;
register slot = 0;
if (p = rindex (name, '/'))
++p;
else
p = name;
setutent();
while ((u = getutent()) != NULL && strcmp(p, u->ut_line) != 0) {
++slot;
}
if (u == NULL)
return 0;
return slot;
}
static void
InitUtmp()
{
struct utmp *ret;
RealSlot = FindUtmp(RealTtyName); /* find current logon slot */
DO DEBUG("InitUtmp(): RealSlot = %d\n", RealSlot);
if (RealSlot) {
ReadUtmp(RealSlot, &RealUtmp);
RemoveUtmp(RealSlot);
} else
RealUtmp.ut_type = DEAD_PROCESS;
}
static void
RestoreRealUtmp()
{
if (RealUtmp.ut_type != EMPTY) {
struct utmp *ret;
#ifdef AUX
int wtmpfd;
#endif
setutent();
RealUtmp.ut_type = USER_PROCESS;
pututline(&RealUtmp);
#ifdef AUX
if ((wtmpfd = open(WtmpName,1)) >= 0) {
lseek (wtmpfd, 0L, 2);
write (wtmpfd, (char *)&RealUtmp, sizeof(RealUtmp));
close (wtmpfd);
}
#endif
} else
DO DEBUG("RestoreRealUtmp(): RealUtmp.ut_type == EMPTY\n");
}
static int
SetUtmp(char *tty, int isreal, int lpid)
{
register char *p;
struct utmp ut, *ret;
#ifdef AUX
int wtmpfd;
#endif
DO DEBUG("SetUtmp(): tty = %s, isreal = %d, lpid = %d\n", tty, isreal, lpid);
if (p = rindex(tty, '/'))
++p;
else
p = tty;
setutent();
strncpy(ut.ut_line, p, 8);
ret = getutline(&ut);
if (ret == NULL) {
DO DEBUG("SetUtmp(): cannot locate utmp entry for %s\n", p);
ret = &ut;
}
strncpy(ret->ut_line, p, 8);
#ifdef AUX
ret->ut_id[0] = 'p';
ret->ut_id[1] = 't';
ret->ut_id[2] = ret->ut_line[3];
ret->ut_id[3] = ret->ut_line[4];
#endif
strncpy(ret->ut_user, LoginName, 8);
if (RealSlot && isreal)
ret->ut_time = RealUtmp.ut_time; /* use original login time */
else
time(&(ret->ut_time));
ret->ut_pid = lpid;
ret->ut_type = USER_PROCESS;
pututline(ret);
#ifdef AUX
if ((wtmpfd = open(WtmpName,1)) >= 0) {
lseek (wtmpfd, 0L, 2);
write (wtmpfd, (char *)ret, sizeof(*ret));
close (wtmpfd);
}
#endif
return FindUtmp(p);
}
static void RemoveTtyFromUtmp(struct Layer *l)
{
struct utmp ut, *ret;
#ifdef AUX
int wtmpfd;
#endif
setutent();
ut_tty_cpy(&ut, l->tty);
ret = getutline(&ut);
if (ret) {
ret->ut_type = DEAD_PROCESS;
pututline(ret);
}
#ifdef AUX
if ((wtmpfd = open(WtmpName,1)) >= 0) {
lseek (wtmpfd, 0L, 2);
write (wtmpfd, (char *)ret, sizeof(*ret));
close (wtmpfd);
}
#endif
}
#else
#ifndef SVR4
static char UtmpName[] = "/etc/utmp";
static int RealSlot = 0; /* Real tty logon slot */
static int
FindUtmp(name)
char *name;
{
register char *p;
register struct ttyent *tp;
register slot;
DO DEBUG("FindUtmp(%s)\n", name);
slot = 1;
if (!utmp)
return 0;
if (p = rindex (name, '/'))
++p;
else
p = name;
setttyent ();
while ( (tp = getttyent ()) != NULL
&& strcmp (p, tp->ty_name) != 0
)
++slot;
if (tp == NULL)
return 0;
DO DEBUG(" slot %d\n", slot);
return slot;
} /* FindUtmp() */
static int
SetUtmp (name, mainlogin, lpid)
char *name; /* tty name */
int mainlogin; /* this is primary login */
int lpid;
{
register char *p;
register slot;
struct utmp u;
if ((slot=FindUtmp(name)) == 0)
return ( 0 );
if (p = rindex (name, '/'))
++p;
else
p = name;
strncpy (u.ut_line, p, 8);
strncpy (u.ut_name, LoginName, 8);
strncpy(u.ut_host, Filename (RealTtyName), 16); /* host is real tty */
if (RealSlot && mainlogin)
u.ut_time = RealUtmp.ut_time; /* use original login time */
else
time (&u.ut_time);
(void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
(void) write (utmpf, (char *)&u, sizeof (u));
return ( slot );
} /* SetUtmp() */
static int
ReadUtmp(slot, entry)
int slot; /* slot to read */
struct utmp *entry; /* entry to read into */
{
int cnt; /* return count */
if (!utmp)
exit(EXITABNORMAL); /* no utmp access */
(void) lseek(utmpf, (long)(slot * sizeof(struct utmp)), 0);
cnt = read(utmpf, (char *)entry, sizeof(struct utmp));
DO DEBUG("ReadUtmp cnt %d, errno %d, line %.8s, name %.8s, host %.16s\n",
cnt, errno, entry->ut_line, entry->ut_name, entry->ut_host);
return ( cnt );
} /* ReadUtmp() */
static void
WriteUtmp(slot, entry)
int slot; /* slot to write */
struct utmp *entry; /* entry to write from */
{
int cnt; /* write return code */
if (!utmp)
return; /* no utmp access */
(void) lseek(utmpf, (long)(slot * sizeof(struct utmp)), 0);
cnt = write(utmpf, (char *)entry, sizeof(struct utmp));
DO DEBUG("WriteUtmp() slot %d cnt %d line %.8s name %.8s host %.16s\n",
slot, cnt, entry->ut_line, entry->ut_name, entry->ut_host);
} /* WriteUtmp() */
static void
RemoveUtmp (slot)
int slot;
{
struct utmp u;
if (slot)
{ bzero ((char *)&u, sizeof (u));
(void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
(void) write (utmpf, (char *)&u, sizeof (u));
}
} /* RemoveUtmp() */
static void
RemoveTtyFromUtmp(layer)
struct Layer *layer;
{
RemoveUtmp(layer->slot);
}
static void
InitUtmp ()
{
if ((utmpf = open (UtmpName, O_RDWR)) == -1)
{ if (errno != EACCES)
Msg (errno, UtmpName);
return;
}
utmp = 1;
RealSlot = FindUtmp(RealTtyName); /* find current logon slot */
if (RealSlot)
{ if (ReadUtmp(RealSlot, &RealUtmp) > 0) /* read real login utmp */
RemoveUtmp(RealSlot); /* remove original logon slot */
else
RealSlot = 0; /* something's wrong */
}
}
static void
RestoreRealUtmp()
{
if (RealSlot)
WriteUtmp(RealSlot, &RealUtmp); /* restore original login */
}
#endif /* !SVR4 */
#endif
#ifdef SVR4
static void
ut_tty_cpy(struct utmpx *up, char * tty)
{
strncpy(up->ut_line, strncmp(tty, "/dev/", 5) == 0 ? tty + 5 : tty,
sizeof up->ut_line);
up->ut_line[(sizeof up->ut_line) - 1] = '\0';
}
static void
InitUtmp()
{
struct utmpx *ret;
setutxent();
ut_tty_cpy(&RealUtmp, RealTtyName);
if ((ret = getutxline(&RealUtmp)) != NULL) {
RealUtmp = *ret;
RealUtmp.ut_type = LOGIN_PROCESS;
pututxline(&RealUtmp);
} else
RealUtmp.ut_type = EMPTY;
endutxent();
}
static void
RestoreRealUtmp()
{
if (RealUtmp.ut_type == LOGIN_PROCESS) {
struct utmpx *ret;
setutxent();
ret = getutxline(&RealUtmp);
if (ret != NULL) {
RealUtmp.ut_type = USER_PROCESS;
pututxline(&RealUtmp);
}
endutxent();
}
}
static int
SetUtmp(char *tty, int chan, int lpid)
{
struct utmpx ut, *ret;
char buf[8];
setutxent();
ut_tty_cpy(&ut, tty);
ret = getutxline(&ut);
if (ret == NULL)
ret = &ut;
*ret = RealUtmp;
ut_tty_cpy(ret, tty);
(void) sprintf(buf, "ml%02d", chan);
(void) strncpy(ret->ut_id, buf, sizeof(ret->ut_id));
ret->ut_pid = lpid;
ret->ut_type = USER_PROCESS;
if (chan != 1)
gettimeofday(&ret->ut_tv);
(void) strncpy(ret->ut_host, RealTtyName, sizeof(ret->ut_host));
ret->ut_syslen = strlen(RealTtyName) + 1;
pututxline(ret);
endutxent();
}
static void
RemoveTtyFromUtmp(struct Layer *l)
{
struct utmpx ut, *ret;
char buf[8];
setutxent();
memset(&ut, '\0', sizeof(ut));
ut_tty_cpy(&ut, l->tty);
(void) sprintf(buf, "ml%02d", l->chan);
(void) strncpy(ut.ut_id, buf, sizeof(ut.ut_id));
ret = getutxline(&ut);
if (ret != NULL) {
ret->ut_type = DEAD_PROCESS;
pututxline(ret);
}
endutxent();
}
#endif /* SVR4 */